import time


def _make_sec(time):
  assert type(time) == int or type(time) == float, type(time)
  if (time >= 1e9 and time < 2e9):
    return time
  elif (time >= 1e18 and time < 2e18):
    return time / 1e9
  else:
    raise ValueError(time)


# https://en.wikipedia.org/wiki/Token_bucket
# https://stackoverflow.com/questions/667508/whats-a-good-rate-limiting-algorithm
class QtyLimiter(object):
  def __init__(self, rate_qty, per, base_time=None):
    self.rate_qty = float(rate_qty)  # unit: messages
    self.per = float(per)  # unit: seconds
    self.allowance = rate_qty  # unit: messages
    if base_time is None:
      base_time = time.time()
    self.last_check = _make_sec(base_time)  # floating-point, e.g. usec accuracy. Unit: seconds

  def check(self, current=None, new_qty=None):
    rate_discount = new_qty
    current = time.time() if current is None else _make_sec(current)
    time_passed = current - self.last_check
    if time_passed <= 0:
      return False
    self.last_check = current
    self.allowance += time_passed * (self.rate_qty / self.per)
    if (self.allowance > self.rate_qty):
      self.allowance = self.rate_qty  # throttle
    if (self.allowance < rate_discount):
      return False
    else:
      self.allowance -= rate_discount
      return True

  def approve(self, current=None, new_qty=None):
    rate_discount = new_qty
    current = time.time() if current is None else _make_sec(current)
    time_passed = current - self.last_check
    if time_passed <= 0:
      return False
    allowance = self.allowance + time_passed * (self.rate_qty / self.per)
    if (allowance > self.rate_qty):
      allowance = self.rate_qty  # throttle
    if (allowance < rate_discount):
      return False
    else:
      return True
